package mskruch.gps.calculation;

import gpstools.TrainingPoint;
import mskruch.gps.Point;
import mskruch.gps.PointFactory;

/**
 * Cadence calculator
 * 
 * @author Marcin Skruch
 * 
 */
public class CadenceCalculator {
    /** time in ms */
    private long time = 0;
    /** time * cad sum */
    private double sum = 0;
    /** maximum cad */
    private short max = 0;
    /** minimum cad */
    private short min = 0;

    /** most recent added point */
    private Point last = null;

    CadenceCalculator() {
    }

    /**
     * add a point to calculation
     * 
     * @param point
     *            gps point
     */
    public CadenceCalculator add(final Point point) {
        if (hasValidCadenceData(point)) {
            if (last != null) {
                if (point.getTime() < last.getTime()) {
                	return this;
                }

                final double timeDiff = point.getTime() - last.getTime();
                final short cad = point.getCadence().shortValue();

                this.sum += timeDiff * cad;
                this.time += timeDiff;

                if (cad < min) {
                    min = cad;
                } else if (cad > max) {
                    max = cad;
                }
            } else {
                /* first point */
                min = max = point.getCadence();
            }
        }
        last = point;
        return this;
    }

    private boolean hasValidCadenceData(Point point) {
        return point.getCadence() != null && point.getCadence() > 0;
    }

    /**
     * returns average cadence
     * 
     * @return average cadence or <code>null</code> when no points were added to
     *         calculation
     */
    public Short getAverage() {
        if (time == 0 && last == null) {
            /* no data */
            return null;
        } else if (time == 0) {
            /* only one point */
            return last.getCadence();
        } else {
            /* many points */
            return (short) Math.round(sum / time);
        }
    }

    /**
     * returns minimum cadence
     * 
     * @return minimum cadence or <code>null</code> when no points were added to
     *         calculation
     */
    public Short getMinimum() {
        if (last == null) {
            /* no data */
            return null;
        } else {
            return min;
        }
    }

    /**
     * returns maximum cadence
     * 
     * @return maximum cadence or <code>null</code> when no points were added to
     *         calculation
     */
    public Short getMaximum() {
        if (last == null) {
            /* no data */
            return null;
        } else {
            return max;
        }
    }

    public void add(TrainingPoint point) {
        add(PointFactory.newPoint(point));
    }

}
